Skip to content

fix(cli): validate Python identifier for code-based agent creation - #7496

Open
philipp-horstenkamp wants to merge 3 commits into
google:mainfrom
philipp-horstenkamp:fix/cli-create-identifier-validation
Open

philipp-horstenkamp wants to merge 3 commits into
google:mainfrom
philipp-horstenkamp:fix/cli-create-identifier-validation

Conversation

@philipp-horstenkamp

@philipp-horstenkamp philipp-horstenkamp commented Oct 10, 2026 •

Copy link
Copy Markdown

Code-based agents created via adk create require dynamic module imports (e.g. importlib.import_module), which fail with ModuleNotFoundError when given kebab-case names or Python keywords.

Add validation in cli_create.run_cmd ensuring code-based agent names are valid Python identifiers and not reserved keywords, aborting before any interactive dialogs. Leaves validate_app_name and config-based YAML agents unchanged to avoid breaking changes.

Please ensure you have read the contribution guide before creating a pull request.

Link to Issue or Description of Change

1. Link to an existing issue (if applicable):

N/A (no existing issue filed yet)

2. Or, if no issue exists, describe the change:

Problem:
When running adk create my-agent (which defaults to --type=code), the CLI accepts the agent name without warning and generates the directory my-agent/ with agent.py and __init__.py.

However, because hyphens (-) are invalid in Python identifiers, subsequent commands like adk run my-agent or adk deploy attempt dynamic module imports via importlib.import_module(f"{agent_name}.agent") and fail immediately:

ModuleNotFoundError: No module named 'my-agent'

Currently, adk create relies exclusively on validate_app_name from google.adk.apps.app, which permits hyphens (_VALID_APP_NAME_RE = re.compile(r"^[a-zA-Z][a-zA-Z0-9_-]*$")).

Solution:
Rather than modifying validate_app_name (which would be a breaking change for existing applications using hyphens in App(name="...") session keys, artifact storage, and telemetry), this PR scopes Python identifier validation to code-based agent creation in cli_create.py:

  1. Python Identifier Validation for Code Agents:
    • Rejects kebab-case names with a clear error:
      Invalid agent name '<name>': code-based agents must be a valid Python identifier (e.g. 'my_agent', not 'my-agent').
    • Accepts valid Python identifiers (snake_case, PascalCase, and camelCase).
  2. Early Abort Prior to Dialogs:
    • The agent type is resolved upfront, allowing validation to fail immediately before prompting the user with interactive dialogs (folder overwrite confirmation, model selection, or GCP onboarding).
  3. Preserves YAML Config Agents:
    • YAML config agents (root_agent.yaml) are loaded by filesystem path rather than importlib, so kebab-case folder names remain supported for --type=config.

Testing Plan

Unit Tests:

  • I have added or updated unit tests for my change.
  • All unit tests pass locally.

Summary of passed pytest results:

tests\unittests\cli\utils\test_cli_create.py ........................... [ 58%]
...................                                                      [100%]
============================= 46 passed in 15.34s =============================

Added tests in tests/unittests/cli/utils/test_cli_create.py:

  • test_run_cmd_rejects_non_identifier_or_keyword_agent_name: Verifies rejection of kebab-case and reserved keywords, and confirms that interactive prompts (e.g. _prompt_for_model) are never reached.
  • test_run_cmd_accepts_valid_identifier_names: Verifies that snake_case, PascalCase, and camelCase succeed and create agent.py.
  • test_run_cmd_allows_kebab_case_for_config_agents: Verifies that --type=config continues to support kebab-case and generates root_agent.yaml with expected contents.
  • test_run_cmd_rejects_kebab_case_when_prompted_as_code: Verifies that choosing CODE in the interactive prompt still triggers validation.

Manual End-to-End (E2E) Tests:

  1. Run adk create my-agent:
    • Defaults to code and rejects immediately with: Error: Invalid agent name 'my-agent': code-based agents must be a valid Python identifier (e.g. 'my_agent', not 'my-agent') and cannot be a Python keyword.
  2. Run adk create my-agent --type=config:
    • Successfully generates my-agent/root_agent.yaml.
  3. Run adk create my_agent:
    • Prompts for model and successfully creates my_agent/agent.py.

Checklist

  • I have read the CONTRIBUTING.md document.
  • I have performed a self-review of my own code.
  • I have commented my code, particularly in hard-to-understand areas.
  • I have added tests that prove my fix is effective or that my feature works.
  • New and existing unit tests pass locally with my changes.
  • I have manually tested my changes end-to-end.
  • Any dependent changes have been merged and published in downstream modules.

Additional context

This PR is intentionally designed as a zero-breaking hotfix. If maintainers prefer to unify application naming rules framework-wide or enforce strict lowercase snake_case (PEP 8) for all agents, that broader design discussion should follow on maintainer level.

Code-based agents created via adk create require dynamic module imports (e.g. importlib.import_module), which fail with ModuleNotFoundError when given kebab-case names or Python keywords.

Add validation in cli_create.run_cmd ensuring code-based agent names are valid Python identifiers and not reserved keywords, aborting before any interactive dialogs. Leaves validate_app_name and config-based YAML agents unchanged to avoid breaking changes.

@BichengWang BichengWang left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] Preserve creation of keyword-named applications

keyword.iskeyword(app_name) rejects application folder names that the current loader supports. At base 3c31fa5100a617d03a19f8a9495b8b93d2e47628, the real CLI command adk create class --type code --model gemini-2.5-flash --api_key offline-placeholder generates an application that AgentLoader(<parent>).load_agent('class') successfully loads as an LlmAgent named root_agent; import works too. At this head, both create commands exit 2 before generating files, while the head loader still successfully loads those base-generated applications. Dynamic imports accept keyword strings, and the generated agent's name remains root_agent, so this introduces a compatibility restriction on working local applications. Please remove the keyword check while retaining isidentifier() for hyphens, and cover creation followed by real loading for a keyword folder. Verified offline with fake credentials and a socket/DNS/subprocess guard; model inference and deployment were not tested.

…tifiers

Remove keyword.iskeyword check in cli_create to allow applications named after Python keywords, which AgentLoader and dynamic imports support. Retain isidentifier check to reject hyphenated kebab-case folder names.
@philipp-horstenkamp

Copy link
Copy Markdown
Author

@BichengWang i remove the keyword part. Sorry for it. That comes from listening to AI and not just doing what the original plan was.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants